﻿/************************************************************************
 * AIDE - Automata Identification Engine
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     AIDE\OnlineAlgo\IAs\IA_Learner.cs
 * Description  :     Interface Automata Learner
 * Change log   :     -May 2013:  Initial version
 * 					  -Jun 2014:  Add the configuration	
 * To Do        :
 ************************************************************************/
using System;
using System.Collections.Generic;
using SMLib.Automata.Base;
using SMLib.Automata.IA;
using SMLib.Automata.MM;
using AIDE.Oracles.NFMs;
using AIDE.OnlineAlgo.NFMs;

/// <summary>
/// Online Learning algorithms for interface automata
/// </summary>
namespace AIDE.Learning.IA_Learning
{
    /// <summary>
    /// Learner for a realistic (non-deterministic) Interface Automata
    /// </summary>
    public class IA_Learner
    {
		/// <summary>
		/// The Mealy machine learner
		/// </summary>
        INFM_Learner learner;

        /// <summary>
        /// The wrapper of IA as NFM
        /// </summary>
        IAWrapperInNFM wrapper;

        /// <summary>
        /// The NFM being learnt
        /// </summary>
        MealyMachine nfm;

        /// <summary>
        /// The learnt IA
        /// </summary>
        InterfaceAutomaton leartModel;

        TimeSpan ts;
		
		/// <summary>
		/// Algorithms
		/// </summary>
		//enum Algos {NSTAR,LPlusM,LOne};
		//Algos useAlgo;
		
        /// <summary>
        /// Constructor [an automaton, then we use an emulator 
        /// </summary>
        /// <param name="automaton">the automaton to be learnt</param>
        public IA_Learner(InterfaceAutomaton automaton)
        {
            IBlackBox_IA sul = new SUL_Emulator_IA(automaton);			
			init(sul);					
        }
		
		 /// <summary>
        /// Constructor [a black box system]
        /// </summary>
        /// <param name="sulStepper">the system under learning as a black box IA system</param>
        public IA_Learner(IBlackBox_IA sul)
        {
			init (sul);
        }
		
		
		private void init(IBlackBox_IA sul)
		{

            nfm = null;
            leartModel = null;
			wrapper = new IAWrapperInNFM(sul);
			string learningAlgo=AIDE.Base.LibSetting.ConfOf("AIDE.IA_LearningAlgo");


			if(learningAlgo==null)
				throw new Exception("The learning algorithm is not defined!");
			else if(learningAlgo=="NStar")
			{
				//useAlgo = Algos.NSTAR;				
                Oracle_NMAT_Realistic nfmOracle = new Oracle_NMAT_Realistic(wrapper);
                learner = new N_Star(nfmOracle);
			}
			else if(learningAlgo=="LPlusM")
			{
				//useAlgo = Algos.LPlusM;				
				Oracle_MAT_Realistic dfmOracle = new Oracle_MAT_Realistic(wrapper);
                learner = new L_Plus_M(dfmOracle);
			}
			else if(learningAlgo=="LOne")
			{
				//useAlgo = Algos.LOne;
		        Oracle_MAT_Realistic dfmOracle = new Oracle_MAT_Realistic(wrapper);
                learner = new L_One(dfmOracle);
			}
		}

        /// <summary>
        /// Learn the SUL
        /// </summary>
        /// <returns>the learnt model</returns>
        public InterfaceAutomaton Learn()
        {
            DateTime now = DateTime.Now;
            //* first learn the mealy machine
			nfm = LearnMealy();
            /*try
            {
                nfm = LearnMealy();
            }
            catch (Exception e)
            {
                Console.WriteLine("Oops! An exception happened:" + e);
                ts = DateTime.Now - now;
                Console.WriteLine("Total time till exception:" + ts.ToString());
                return null;
            }*/

            //* and then, convert it to the IA!
            leartModel = IAWrapperInNFM.ConvertToIOLTS(nfm);
            ts = DateTime.Now - now;

            //OnlineRandomWalkTesting tcg = new OnlineRandomWalkTesting(leartModel, this.sulStepper);
            //tcg.CheckConformance();

            //Visualization.Visualization viewer = new Visualization.Visualization(nfm);
            //Visualization.Visualization viewer = new Visualization.Visualization(leartModel);
            //viewer.ShowDialog();

            return leartModel;
        }

        /// <summary>
        /// Log of the learnt process
        /// </summary>
        /// <returns>log as a string</returns>
        public string Log()
        {
            if (nfm == null)
                return "";
            string log = "";
            //log += "\n----------------------------------";
            log += "\nTime of Learning=" + ts.ToString();
            log += "\nLearnt IA:\n\t #states=" + leartModel.Size;
            //* Count number of non-deterministic states
            int numberOfND = 0;
            for (int i = 0; i < leartModel.Size; i++)
            {
                bool hasEv = false;
                for (int j = 0; j < leartModel[i].Transitions.Count; j++)
                {
                    if (((InterfaceAutomaton.IA_Step)leartModel[i].Transitions[j]).StepType == InterfaceAutomaton.IA_Step.Step_Type.EVENT)
                    {
                        if (!hasEv)
                        {
                            hasEv = true;
                        }
                        else
                        {
                            numberOfND++;
                            break;
                        }
                    }
                }
            }
			/*
            log += "\n\t #ND states=" + numberOfND;
            log += "\n\t #OQ: " + nfmOracle.numberOfMQ + " #EQ: " + nfmOracle.numberOfEQ;
            log += "\n\t #EQ Time: " + nfmOracle.EqTime + " #OQTime: " + nfmOracle.OqTime;
            log += "\n\tIO Time:" + nfmOracle.IOTime;
            log += "\n\tNumber Of resets By N*:" + nfmOracle.numberOfResetsbyNStar;
            log += "\n\tNumber Of resets By N* for OQ:" + nfmOracle.numberOfExperimentForOQ;
            log += "\n\tNumber Of resets By N* for EQ:" + nfmOracle.numberOfExperimentForEQ;
			*/
            //log += nfmLearner.Log();

            return log;
        }

        /// <summary>
        /// learn the mealy machine
        /// </summary>
        /// <returns>the learnt mealy machine</returns>
        MealyMachine LearnMealy()
        {
            return learner.Learn();
        }
		
		/*
        public void FlagAsDeterministic(List<ISymbol> list)
        {
            nfmOracle.FlagInputsAsDeterministic(list);
        }
        */
    }
}
